"""
工具类 - 包含项目中常用的通用方法
"""
import os
import re
import time
import random
import string
import logging
from functools import wraps
from datetime import datetime, timedelta
from werkzeug.utils import secure_filename
from werkzeug.security import generate_password_hash, check_password_hash
from flask import current_app, abort, request, flash, redirect, url_for
from flask_login import current_user
from app import db

# 创建日志记录器
logger = logging.getLogger('zhiyi')

class SecurityUtil:
    """安全相关工具类"""
    
    @staticmethod
    def generate_password_hash(password):
        """生成密码哈希"""
        return generate_password_hash(password)
    
    @staticmethod
    def check_password_hash(password_hash, password):
        """验证密码哈希"""
        return check_password_hash(password_hash, password)
    
    @staticmethod
    def generate_random_token(length=32):
        """生成随机令牌"""
        return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length))

class AuthUtil:
    """认证相关工具类"""
    
    @staticmethod
    def require_login(view_func):
        """装饰器: 确保用户已登录才能访问视图函数，未登录则重定向到登录页面并缓存原请求URL"""
        @wraps(view_func)
        def decorated_view(*args, **kwargs):
            if not current_user.is_authenticated:
                flash('请先登录再访问此页面', 'info')
                return redirect(url_for('auth.login', next=request.url))
            return view_func(*args, **kwargs)
        return decorated_view
    
    @staticmethod
    def require_owner(model_class):
        """装饰器: 确保当前用户是资源所有者
        
        参数:
            model_class: 要检查所有权的数据模型类
        
        要求模型具有user_id字段，检查是否与当前用户ID匹配
        """
        def decorator(view_func):
            @wraps(view_func)
            def decorated_view(id, *args, **kwargs):
                resource = model_class.query.get_or_404(id)
                if not hasattr(resource, 'user_id') or resource.user_id != current_user.id:
                    abort(403)
                return view_func(id, *args, **kwargs)
            return decorated_view
        return decorator

class FileUtil:
    """文件处理工具类"""
    
    @staticmethod
    def allowed_file(filename, allowed_extensions):
        """检查文件是否有允许的扩展名"""
        return '.' in filename and \
            filename.rsplit('.', 1)[1].lower() in allowed_extensions
    
    @staticmethod
    def save_file(file, upload_folder, allowed_extensions=None):
        """保存上传的文件并返回安全的文件名
        
        添加时间戳防止文件名冲突，确保目录存在
        """
        if not file:
            return None
            
        # 检查扩展名
        if allowed_extensions and not FileUtil.allowed_file(file.filename, allowed_extensions):
            return None
            
        # 确保文件名安全
        filename = secure_filename(file.filename)
        
        # 添加时间戳防止文件名冲突
        timestamp = int(time.time())
        name, ext = os.path.splitext(filename)
        filename = f"{name}_{timestamp}{ext}"
        
        # 确保目录存在
        if not os.path.exists(upload_folder):
            os.makedirs(upload_folder)
            
        # 保存文件
        file_path = os.path.join(upload_folder, filename)
        file.save(file_path)
        
        return filename

class StringUtil:
    """字符串处理工具类"""
    
    @staticmethod
    def truncate(text, length=100, suffix='...'):
        """截断字符串到指定长度"""
        if text is None:
            return ''
        if len(text) <= length:
            return text
        return text[:length].rstrip() + suffix
    
    @staticmethod
    def strip_html(text):
        """移除HTML标签"""
        if text is None:
            return ''
        return re.sub(r'<[^>]*>', '', text)
    
    @staticmethod
    def slugify(text):
        """生成URL友好的slug"""
        if text is None:
            return ''
        text = text.lower()
        # 替换非字母数字字符为连字符
        text = re.sub(r'[^a-z0-9]+', '-', text)
        # 移除头尾连字符
        return text.strip('-')

class DateUtil:
    """日期时间处理工具类"""
    
    @staticmethod
    def format_datetime(dt, format='%Y-%m-%d %H:%M:%S'):
        """格式化日期时间"""
        if dt is None:
            return ''
        return dt.strftime(format)
    
    @staticmethod
    def time_ago(dt):
        """计算时间差的人性化表示"""
        if dt is None:
            return ''
            
        now = datetime.utcnow()
        diff = now - dt
        
        if diff < timedelta(minutes=1):
            return '刚刚'
        elif diff < timedelta(hours=1):
            return f'{diff.seconds // 60} 分钟前'
        elif diff < timedelta(days=1):
            return f'{diff.seconds // 3600} 小时前'
        elif diff < timedelta(days=30):
            return f'{diff.days} 天前'
        elif diff < timedelta(days=365):
            return f'{diff.days // 30} 个月前'
        else:
            return f'{diff.days // 365} 年前'

class LogUtil:
    """日志记录工具类"""
    
    @staticmethod
    def info(message, **kwargs):
        """记录信息日志"""
        logger.info(message, extra=kwargs)
    
    @staticmethod
    def warning(message, **kwargs):
        """记录警告日志"""
        logger.warning(message, extra=kwargs)
    
    @staticmethod
    def error(message, **kwargs):
        """记录错误日志"""
        logger.error(message, extra=kwargs)
    
    @staticmethod
    def exception(message, **kwargs):
        """记录异常日志"""
        logger.exception(message, extra=kwargs)

class DataUtil:
    """数据处理工具类"""
    
    @staticmethod
    def paginate(query, page, per_page):
        """对查询进行分页"""
        return query.paginate(page=page, per_page=per_page, error_out=False)
    
    @staticmethod
    def filter_by_tag(query, tag_id):
        """按标签过滤查询"""
        from app.models.question import Question
        from app.models.question_tag import QuestionTag
        
        return query.join(QuestionTag).filter(QuestionTag.tag_id == tag_id)
    
    @staticmethod
    def search(query, search_term, fields):
        """高级搜索实现
        
        功能:
        - 支持空格分词进行多关键词搜索
        - 支持多字段组合搜索
        - 支持#标签语法进行标签搜索
        
        搜索逻辑:
        - 不同关键词之间是AND关系
        - 同一关键词在不同字段之间是OR关系
        - 标签搜索需要精确匹配标签名
        """
        if not search_term:
            return query
            
        # 分词处理
        keywords = search_term.strip().split()
        filters = []
        
        for keyword in keywords:
            # 处理标签搜索
            if keyword.startswith('#'):
                tag_name = keyword[1:]
                from app.models.tag import Tag
                from app.models.question_tag import QuestionTag
                tag = Tag.query.filter(Tag.name.like(f"%{tag_name}%")).first()
                if tag:
                    query = query.join(QuestionTag).filter(QuestionTag.tag_id == tag.id)
                continue
                
            # 普通关键词搜索
            keyword = f"%{keyword}%"
            field_filters = []
            
            for field in fields:
                field_filters.append(field.like(keyword))
                
            # 对每个关键词使用OR连接不同字段
            filters.append(db.or_(*field_filters))
        
        # 对不同关键词使用AND连接
        if filters:
            query = query.filter(db.and_(*filters))
            
        return query 